//----------------------------------------------
//            Math Parser Advanced
// Copyright © 2012-2013 Fat Cat Gameworks
// 
// Valid Functions:
// 	Minimum	: <min>value1,value2,...</min>		returns lesser of input values
//	Maximum	: <max>value1,value2,...</max>		returns greater of input values
//	Random	: <random>value</random>			returns float 0 inclusive through value exclusive
//	Range	: <range>min,max</range>			returns integer from min to max inclusive
//	Floor	: <floor>value</floor>				returns float clamped to lesser int
//	Ceiling	: <ceil>value</ceil>				returns float clamped to greater int
//	Absolute: <abs>value</abs>					returns absolute value of input
//	Set Var	: <set>name,input</set>				sets value of variable to input, returns input
//----------------------------------------------

using System.Xml.XPath;
using System.Xml;
using UnityEngine;

public class MathParser {
	
	static XmlDocument doc;
	static XPathNavigator nav;
	
	static MathParser()
	{
		doc = new XmlDocument();
		nav = doc.CreateNavigator();
		nav.AppendChild("<Root/>");
		nav.MoveToChild("Root","");
		XmlNamespaceManager nsm = new XmlNamespaceManager(nav.NameTable);
		nsm.AddNamespace("MathParser","");
	}
	
	public static void SetVariable(string variableName, float variableValue)
	{
		XPathNavigator nodeNav = nav.SelectSingleNode("//MathParser:"+variableName);
		if(nodeNav != null)
			nodeNav.SetValue(variableValue.ToString());
		else
			nav.AppendChild("<"+variableName+">"+variableValue+"</"+variableName+">");
	}
	
	public static float Evaluate(string expressionString)
	{
		//try
		{
			string expression = expressionString;
			Debug.Log("Eval Sets: " + expression);
			expression = EvaluateSets(expression);
			Debug.Log("Eval Mins: " + expression);
			expression = EvaluateMinimums(expression);
			Debug.Log("Eval Maxs: " + expression);
			expression = EvaluateMaximums(expression);
			Debug.Log("Eval Rands: " + expression);
			expression = EvaluateRandoms(expression);
			Debug.Log("Eval Range: " + expression);
			expression = EvaluateRandomRange(expression);
			Debug.Log("Eval Ceils: " + expression);
			expression = EvaluateCeils(expression);
			Debug.Log("Eval Floors: " + expression);
			expression = EvaluateFloors(expression);
			Debug.Log("Eval Absolutes: " + expression);
			expression = EvaluateAbsolutes(expression);
			Debug.Log("Eval Final: " + expression);
			
			string modExp = new System.Text.RegularExpressions.Regex(@"([\+\-\*])").Replace(expression, " ${1} ").Replace("/", " div ").Replace("%", " mod ");
			return (float)(double)nav.Evaluate(modExp + "+0");
		}
		//catch
		//{
		//	Debug.LogError("There was an error while parsing math function:\n	" + expressionString);
		//	return float.NaN;
		//}
	}
	
	public static string EvaluateSets(string expressionString)
	{
		string[] parts = System.Text.RegularExpressions.Regex.Split(expressionString,"<set>");
		if(parts.Length == 1)
		{
			return expressionString;
		}
		
		string reconstructed = parts[0];
		for(int i = 1; i < parts.Length; i++)
		{
			string[] subParts = System.Text.RegularExpressions.Regex.Split((parts[i]),"</set>");
			string[] setComp = subParts[0].Split(',');
			
			SetVariable(setComp[0], Evaluate(setComp[1]));
			
			reconstructed = reconstructed + 
					setComp[0] + 
					subParts[1];
		}
		
		return reconstructed;
	}
	
	public static string EvaluateMinimums(string expressionString)
	{
		string[] parts = System.Text.RegularExpressions.Regex.Split(expressionString,"<min>");
		if(parts.Length == 1)
		{
			return expressionString;
		}
		
		string reconstructed = parts[0];
		for(int i = 1; i < parts.Length; i++)
		{
			string[] subParts = System.Text.RegularExpressions.Regex.Split((parts[i]),"</min>");
			string[] minComps = subParts[0].Split(',');
			
			if(minComps.Length <= 1)
			{
				reconstructed = reconstructed + Evaluate(subParts[0]) + subParts[1];
			}
			else
			{
				float minVal = float.PositiveInfinity;
				foreach(string v in minComps)
				{
					minVal = Mathf.Min(Evaluate(v), minVal);
				}
				reconstructed = reconstructed + minVal + subParts[1];
			}
		}
		
		return reconstructed;
	}
	
	public static string EvaluateMaximums(string expressionString)
	{
		string[] parts = System.Text.RegularExpressions.Regex.Split(expressionString,"<max>");
		if(parts.Length == 1)
		{
			return expressionString;
		}
		
		string reconstructed = parts[0];
		for(int i = 1; i < parts.Length; i++)
		{
			string[] subParts = System.Text.RegularExpressions.Regex.Split((parts[i]),"</max>");
			string[] minComps = subParts[0].Split(',');
			
			if(minComps.Length <= 1)
			{
				reconstructed = reconstructed + Evaluate(subParts[0]) + subParts[1];
			}
			else
			{
				float maxVal = float.NegativeInfinity;
				foreach(string v in minComps)
				{
					maxVal = Mathf.Max(Evaluate(v), maxVal);
				}
				reconstructed = reconstructed + maxVal + subParts[1];
			}
		}
		
		return reconstructed;
	}
	
	public static string EvaluateRandoms(string expressionString)
	{
		string[] parts = System.Text.RegularExpressions.Regex.Split(expressionString,"<random>");
		if(parts.Length == 1)
		{
			return expressionString;
		}
		
		string reconstructed = parts[0];
		for(int i = 1; i < parts.Length; i++)
		{
			string[] subParts = System.Text.RegularExpressions.Regex.Split((parts[i]),"</random>");
			
			reconstructed = reconstructed + (Random.value * Evaluate(subParts[0])) + subParts[1];
		}
		
		return reconstructed;
	}
	
	public static string EvaluateRandomRange(string expressionString)
	{
		string[] parts = System.Text.RegularExpressions.Regex.Split(expressionString,"<range>");
		if(parts.Length == 1)
		{
			return expressionString;
		}
		
		string reconstructed = parts[0];
		for(int i = 1; i < parts.Length; i++)
		{
			string[] subParts = System.Text.RegularExpressions.Regex.Split((parts[i]),"</range>");
			string[] randComp = subParts[0].Split(',');
			
			reconstructed = reconstructed + 
					Random.Range(Mathf.FloorToInt(Evaluate(randComp[0])),1+Mathf.FloorToInt(Evaluate(randComp[1]))) + 
					subParts[1];
		}
		
		return reconstructed;
	}

	public static string EvaluateFloors(string expressionString)
	{
		string[] parts = System.Text.RegularExpressions.Regex.Split(expressionString,"<floor>");
		if(parts.Length == 1)
		{
			return expressionString;
		}
		
		string reconstructed = parts[0];
		for(int i = 1; i < parts.Length; i++)
		{
			string[] subParts = System.Text.RegularExpressions.Regex.Split((parts[i]),"</floor>");
			
			reconstructed = reconstructed + Mathf.Floor(Evaluate(subParts[0])) + subParts[1];
		}
		
		return reconstructed;
	}
	
	public static string EvaluateCeils(string expressionString)
	{
		string[] parts = System.Text.RegularExpressions.Regex.Split(expressionString,"<ceil>");
		if(parts.Length == 1)
		{
			return expressionString;
		}
		
		string reconstructed = parts[0];
		for(int i = 1; i < parts.Length; i++)
		{
			string[] subParts = System.Text.RegularExpressions.Regex.Split((parts[i]),"</ceil>");
			
			reconstructed = reconstructed + Mathf.Ceil(Evaluate(subParts[0])) + subParts[1];
		}
		return reconstructed;
	}
	
	public static string EvaluateAbsolutes(string expressionString)
	{
		string[] parts = System.Text.RegularExpressions.Regex.Split(expressionString,"<abs>");
		if(parts.Length == 1)
		{
			return expressionString;
		}
		
		string reconstructed = parts[0];
		for(int i = 1; i < parts.Length; i++)
		{
			string[] subParts = System.Text.RegularExpressions.Regex.Split((parts[i]),"</abs>");
			
			reconstructed = reconstructed + Mathf.Abs(Evaluate(subParts[0])) + subParts[1];
		}
		return reconstructed;
	}
	
	///<summary>
	///Allows for a string to have embedded mathematic equations to be evaluated. 
	///Returns reconstructed string. 
	///The input must contain math equations embedded between {} characters. 
	///Curly Braces that do not indicate that they surround math equations must be escaped by a back-slash.
	///</summary>
	public static string EvaluateMathEmbeddedInText(string expressionString)
	{
		expressionString = expressionString.Replace("\\{", "~wasLeftBrace~");
		expressionString = expressionString.Replace("\\}", "~wasRightBrace~");
		
		string[] parts = expressionString.Split('{');
		if(parts.Length == 1)
		{
			expressionString = expressionString.Replace("~wasLeftBrace~", "{");
			expressionString = expressionString.Replace("~wasRightBrace~", "}");
			return expressionString;
		}
		
		string reconstructed = parts[0];
		for(int i = 1; i < parts.Length; i++)
		{
			string[] subParts = (parts[i]).Split('}');
			reconstructed = reconstructed + Evaluate(subParts[0]) + subParts[1];
		}
		
		reconstructed = reconstructed.Replace("~wasLeftBrace~", "{");
		reconstructed = reconstructed.Replace("~wasRightBrace~", "}");
		
		return reconstructed;
	}

}
